
using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using GruntXProductions;
using Viper;
using Viper.Opcodes;

namespace Diamond
{
	public partial class Parser
	{
		public void Parse()
		{
			while(index < tokens.Count) {
				if(peekToken() is Tokens.EOL)
				{
					readToken();
					continue;
				}
				if(peekToken() is Tokens.Statement && peekToken(1) is Tokens.Statement && peekToken(2) is Tokens.openParenthesis)	
					AST.Add(ParseMethod());
				else if (peekToken().ToString() == "external" && peekToken(1) is Tokens.Statement)
					AST.Add(ParseExternalMethod());
				else if (peekToken().ToString() == "operator" && peekToken(1) is Tokens.Statement)
				{
					AST.Add(ParseOperator());
				}
				else if (peekToken().ToString() == "class")
					AST.Add(ParseClass());
			}
		}
		public CodeBlock ParseBlock()
		{
			CodeBlock cb = new CodeBlock();
			while(peekToken() is Tokens.EOL)
				readToken();
			while(peekToken().ToString() != "end") {
				if(peekToken() is Tokens.EOL)
				{
					readToken();
					continue;
				}
				else
					cb.Body.Add(ParseLine());
			}
			readToken();
			return cb;
		}
		public FunctionCall Call()
		{
			FunctionCall funcCall = new FunctionCall();
			funcCall.Target = readToken().ToString();
			if(!(readToken() is Tokens.openParenthesis)) throw new ParserException("Expected (");
			while(!(peekToken() is Tokens.closeParenthesis)) {
					funcCall.Arguments.Add(ParseExpression());
					if(peekToken() is Tokens.Comma) {
						readToken();
						if(peekToken() is Tokens.closeParenthesis)
							throw new ParserException("Unexpected ,");
					}
			}
			readToken();
			return funcCall;
		}
		public SyntaxNode GetNode ()
		{
			if (peekToken() is Tokens.IntLiteral)
			{
				return (new IntLiteral((uint)((Tokens.IntLiteral)readToken()).Value));	
			}
			else if (peekToken() is Tokens.StringLiteral)
			{
				return (new StringLiteral(((Tokens.StringLiteral)readToken()).Value));
			}
			else if (peekToken().ToString() == "ref" && peekToken(1) is Tokens.Statement)
			{
				readToken();
				Ref r = new Ref();
				r.Symbol = readToken().ToString();
				return r;
			}
			else if (peekToken() is Tokens.Statement && peekToken(1) is Tokens.openParenthesis)
			{
				return Call();
			}
			else if (peekToken() is Tokens.openParenthesis)
			{
				readToken();
				Expression exp = ParseExpression();
				if(!(readToken() is Tokens.closeParenthesis))
					throw new ParserException("Expected )");
				return exp;
			}
			else if (peekToken() is Tokens.Statement)
			{
				return new SymbolRef(readToken().ToString());
			}
			
			return null;
		}
		public Expression ParseExpression(bool boolAND = false)
		{
			Expression exp = new Expression();
			while(!(peekToken() is Tokens.EOL)) {
				
				if (peekToken() is Tokens.Equal)
				{
					readToken();
					Equal equ = new Equal();
					equ.Op1 = exp;
					exp = new Expression();
					equ.Op2 = ParseExpression(true);
					exp.Items.Add(equ);
				}
				else if (peekToken() is Tokens.BooleanAnd )
				{
					readToken();
					And equ = new And();
					equ.Op1 = exp;
					exp = new Expression();
					equ.Op2 = ParseExpression(true);
					exp.Items.Add(equ);
				}
				else if (peekToken() is Tokens.NotEqual)
				{	
					readToken();
					NotEqual equ = new NotEqual();
					equ.Op1 = exp;
					exp = new Expression();
					equ.Op2 = ParseExpression(true);
					exp.Items.Add(equ);
				}
				else if (peekToken() is Tokens.GreaterThan)
				{	
					readToken();
					GreaterThan equ = new GreaterThan();
					equ.Op1 = exp;
					exp = new Expression();
					equ.Op2 = ParseExpression(true);
					exp.Items.Add(equ);
				}
				else if (peekToken() is Tokens.LessThan)
				{	
					readToken();
					LessThan equ = new LessThan();
					equ.Op1 = exp;
					exp = new Expression();
					equ.Op2 = ParseExpression(true);
					exp.Items.Add(equ);
				}
				else
				{
					SyntaxNode Node = GetNode();
					if(Node != null )
					{
						if(peekToken() is Tokens.Add)
						{
							readToken();
							Plus pl = new Plus();
							pl.Op1 = Node;
							pl.Op2 = GetNode();
							exp.Items.Add(pl);
						}
						else if(peekToken() is Tokens.Mul)
						{
							readToken();
							Multiply pl = new Multiply();
							pl.Op1 = Node;
							pl.Op2 = GetNode();
							exp.Items.Add(pl);
						}
						else if(peekToken() is Tokens.Sub)
						{
							readToken();
							Subtract pl = new Subtract();
							pl.Op1 = Node;
							pl.Op2 = GetNode();
							exp.Items.Add(pl);
						}
						else if(peekToken() is Tokens.RightBitshift)
						{
							readToken();
							RightBitshift bs = new RightBitshift();
							bs.Op1 = Node;
							bs.Op2 = GetNode();
							exp.Items.Add(bs);
						}
						else if(peekToken() is Tokens.RightBitshift)
						{
							readToken();
							RightBitshift bs = new RightBitshift();
							bs.Op1 = Node;
							bs.Op2 = GetNode();
							exp.Items.Add(bs);
						}
						else if(peekToken() is Tokens.LeftBitshift)
						{
							readToken();
							LeftBitshift bs = new LeftBitshift();
							bs.Op1 = Node;
							bs.Op2 = GetNode();
							exp.Items.Add(bs);
						}
						else 
						{
							exp.Items.Add(Node);	
						}
					}
					else
					{
						break;
					}
				}
			}
			return exp;
		}
		public void CheckEOL()
		{
			if(checkForEOL)
				if(!(readToken() is Tokens.EOL))
					throw new ParserException("Expected EOL! got" + peekToken(-1).ToString());
			
		}
		public DiamondClass ParseClass()
		{
			DiamondClass ret = new DiamondClass();
			readToken();
			ret.Name = readToken().ToString();
			CheckEOL();
			while(peekToken().ToString() != "end")
			{
				while(peekToken() is Tokens.EOL)
					readToken();
				if(peekToken() is Tokens.Statement && peekToken(1) is Tokens.Statement)
				{
					
					ret.Functions.Add(ParseMethod());
				}
			}
			readToken();
			return ret;
		}
		public OperatorOverload ParseOperator() {
				OperatorOverload ret = new OperatorOverload();
				readToken();
				string Type = readToken().ToString();
				string Name = readToken().ToString();
				ret.Type = Type;
				ret.theOperator = Name;
				if(!(readToken() is Tokens.openParenthesis))
                    throw new ParserException("( Expected!");
				while(!(peekToken() is Tokens.closeParenthesis))
				{
					ret.Arguments.Add(ParseDeclaration());
					if(peekToken() is Tokens.Comma)
					{
						readToken();
						if(peekToken() is Tokens.closeParenthesis)
                            throw new ParserException("Unexpected ,");
					}
				}
				readToken();
				ret.Body = ParseBlock();
				return ret;
		}
		public Method ParseMethod() {
			if(peekToken() is Tokens.Statement && peekToken(1) is Tokens.Statement)
			{
				Method ret = new Method();
				string Type = readToken().ToString();
				string Name = readToken().ToString();
				ret.Type = Type;
				ret.Name = Name;
				if(!(readToken() is Tokens.openParenthesis))
					throw new ParserException("( Expected!");
				while(!(peekToken() is Tokens.closeParenthesis))
				{
					ret.Arguments.Add(ParseDeclaration());
					if(peekToken() is Tokens.Comma)
					{
						readToken();
						if(peekToken() is Tokens.closeParenthesis)
                            throw new ParserException("Unexpected ,");
					}
				}
				readToken();
				ret.Body = ParseBlock();
				return ret;
			}
			else
			{
                throw new ParserException("Method expected!");
			}
		}
		public ExternalMethod ParseExternalMethod() {
			if(peekToken() is Tokens.Statement && peekToken() is Tokens.Statement)
			{
				readToken();
				ExternalMethod retr = new ExternalMethod();
				string Type = readToken().ToString();
				string Name = readToken().ToString();
				retr.Type = Type;
				retr.Name = Name;
				if(!(readToken() is Tokens.openParenthesis))
					throw new ParserException("( Expected!");
				while(!(peekToken() is Tokens.closeParenthesis))
				{
					retr.Arguments.Add(ParseDeclaration());
					if(peekToken() is Tokens.Comma)
					{
						readToken();
						if(peekToken() is Tokens.closeParenthesis)
                            throw new ParserException("Unexpected ,");
					}
				}
				readToken();
				CheckEOL();
				return retr;
			}
			else
			{
                throw new ParserException("Method expected!");
			}
		}
		public Declaration ParseDeclaration() {
			if(peekToken() is Tokens.Statement && peekToken() is Tokens.Statement)
			{
				string Type = readToken().ToString();
				string Name = readToken().ToString();
				Declaration decl;
				if(peekToken() is Tokens.Assign)
				{
					readToken();
					decl = new Declaration(Name, Type, ParseExpression());
				}
				else
				{
					decl = new Declaration(Name, Type);
				}
				return decl;
				
			}	
			else
			{
				throw new ParserException("Declaration expected!");
			}
		}
		public SyntaxNode ParseLine ()
		{
			while(peekToken() is Tokens.EOL)
			{
				readToken();
			}
			
			if(peekToken().ToString() == "return")
			{
				readToken();
				Return ret = new Return(); 
				if(peekToken() is Tokens.EOL)
				{
					readToken();
					return ret;
					
				}
				else
				{
					ret.Value = ParseExpression();
					CheckEOL();
					return ret;
				}
			}
			else if(peekToken() is Tokens.Statement && peekToken(1) is Tokens.Statement)
			{
				Declaration decl = ParseDeclaration();
				CheckEOL();
				return decl;
				
			}
			else if (peekToken().ToString() == "asm" && peekToken(1) is Tokens.openParenthesis && peekToken(2) is Tokens.StringLiteral && peekToken(3) is Tokens.closeParenthesis)
			{
				readToken();
				readToken();
				string IL = ((Tokens.StringLiteral)readToken()).Value; readToken();
				InlineIL code = new InlineIL();
				code.IL_Code = IL;
				return code;
			}
			else if (peekToken().ToString() == "if" && peekToken(1) is Tokens.openParenthesis)
			{
				return ParseIf();
				
			}
			else if (peekToken().ToString() == "break")
			{
				readToken();
				CheckEOL();
				return new Break();
			}
			
			else if (peekToken().ToString() == "while" && peekToken(1) is Tokens.openParenthesis)
			{
				readToken(); readToken();
				WhileStatement whilestat = new WhileStatement();
				whilestat.Comparison = ParseExpression();
				if(!(readToken() is Tokens.closeParenthesis))
                    throw new ParserException("Expected )" + peekToken(-1).ToString());
				whilestat.Body = ParseBlock();
				return whilestat;
			}
			else if (peekToken().ToString() == "for" && peekToken(1) is Tokens.openParenthesis)
			{
				readToken(); readToken();
				ForStatement forstat = new ForStatement();
				forstat.Declaration = ParseDeclaration();
                if (!(readToken() is Tokens.Comma)) throw new ParserException("Expected , 1");
				forstat.Comparison = ParseExpression();
				if(!(readToken() is Tokens.Comma)) throw new ParserException("Expected , 2");
				checkForEOL = false;
				forstat.Step = ParseLine();
				checkForEOL = true;
				if(!(readToken() is Tokens.closeParenthesis))
                    throw new ParserException(") expected!");
				forstat.Body = ParseBlock();
				return forstat;
				
			}
			else if (peekToken() is Tokens.Statement && peekToken(1) is Tokens.Assign)
			{
				Assignment asn = new Assignment();
				asn.Name = readToken().ToString();
				readToken();
				asn.Value = ParseExpression();
				CheckEOL();
				return asn;
			}
			else if (peekToken() is Tokens.Statement && peekToken(1) is Tokens.openParenthesis)
			{
				FunctionCall ret = Call();
				CheckEOL();
				return ret;
			}
			else
			{
                throw new ParserException("Parser encountered unexpected token " + peekToken().ToString());
			}
			
		}
	}
}

